(
	function bootstrap ( )
	{
		//	Copy bootstrap functions into the proper namespace.
		//	If 'gc' is present, it can't be deleted, so don't
		//	bother copying it anywhere, because we can't finish
		//	the job. (grumble grumble)

		var cache = { };

		for (var index in this)
		{
			//	V8 drops its extensions into the global namespace
			//	in such a way as to prevent their deletion. Bummer.
			//	This means that we need to keep the global namespace
			//	a tiny bit more cluttered than I wanted.

			if (index !== 'gc' && index !== 'extensions')
			{
				cache [index] = this [index];
				delete this [index];
			}
		}

		//	Drop everything else off into the bootstrap namespace.

		this.extensions.org.icongarden.bootstrap = cache;

		//	We'll need this next function in a couple of places.

		function stringEndsWith (s,e)
		{
			if (s.length < e.length) return false;
			return s.length - e.length === s.indexOf (e);
		}

		//	Traverse the object hierarchy and capture paths to extensions to be loaded.

		var extensionsArray = [ ];

		function traverse (dotPath,parentObject,parentName)
		{
			for (var child in parentObject)
			{	
				if (typeof (parentObject [child]) === 'string')
				{
					if (stringEndsWith (parentObject [child], ".js"))
					{
						extensionsArray.push (dotPath + '.' + child);
					}
					else if (stringEndsWith (parentObject [child], ".so"))
					{
						extensionsArray.push (dotPath + '.' + child);
					}
					else
					{
						extensions.org.icongarden.bootstrap.log ("ignoring putative extension file with unknown filename extension");
						delete parentObject [child];
					}
				}
				else if (dotPath.length)
				{
					traverse (dotPath + '.' + child, parentObject [child], child);
				}
				else
				{
					traverse (dotPath + child, parentObject [child], child);
				}
			}
		}
	
		traverse ('extensions',this.extensions,'extensions');

		//	Randomly shuffle the extensions to be loaded so they are less likely
		//	to acquire inadvertent dependencies on each other.

		function fisherYates (myArray)
		{
			// implementation from http://sedition.com/perl/javascript-fy.html

			var i = myArray.length;
			if (i === 0) return false;

			while (--i)
			{
				var j = Math.floor (Math.random ( ) * (i + 1));
				var tempi = myArray [i];
				var tempj = myArray [j];
				myArray [i] = tempj;
				myArray [j] = tempi;
			}
		}

		fisherYates (extensionsArray);

		function loadExtension (extensionIndex)
		{
			cache.log ("loading extension '" + extensionsArray [extensionIndex] + "'");

			var pathIndex = 0;
			var parentObject = this;
			var splitPath = extensionsArray [extensionIndex].split ('.');

			//	Drill down into the object hierarchy to find the parent of the extension.

			while (pathIndex < splitPath.length - 1)
			{
				parentObject = parentObject [splitPath [pathIndex]];
				pathIndex += 1;
			}

			//	Load the child into the hierarchy and replace its element in the array
			//	with some meta-data we can consult later.

			var childPropertyName = splitPath [splitPath.length - 1];
			var childObject, nativePath = parentObject [childPropertyName];

			//	Make the native path available where the extension can see it while loading.

			var preservedNativePath = this.extensions.org.icongarden.bootstrap.nativePath;
			this.extensions.org.icongarden.bootstrap.nativePath = nativePath;

			//	Replace the dot path in the extensions array with meta-data about the
			//	extension being loaded. We'll add to this later.

			extensionsArray [extensionIndex] = { dotPath : extensionsArray [extensionIndex] };

			try
			{
				if (stringEndsWith (nativePath, ".js"))
				{
					cache.log ("loading script extension " + nativePath);
					childObject = eval ('(' + cache.loadScript (nativePath) + ')');
				}
				else if (stringEndsWith (nativePath, ".so"))
				{
					cache.log ("loading binary extension " + nativePath);
					childObject = cache.loadBinary (nativePath);
				}
				else
				{
					throw "invalid element found in extensions array";
				}
			}
			catch (e)
			{
				cache.log (e);
				delete parentObject [childPropertyName];
			}

			//	Delete the native path to the extension after loading the
			//	extension because nobody else needs to know about it.

			this.extensions.org.icongarden.bootstrap.nativePath = preservedNativePath;

			//	Store a reference to the extension object in the extension
			//	meta-data to signify that we loaded it, and, finally drop
			//	the extension object into the correct place in the tree
			//	and return the accursed thing.

			if (undefined !== childObject)
			{	
				extensionsArray [extensionIndex].childObject = childObject;
				parentObject [childPropertyName] = childObject;
			}

			return childObject;
		}

		//	Make a function available before loading extensions so they
		//	can take explicit dependencies on each other while loading.
		//	Regular scripts can use this as well as a sort of assertion.

		this.extensions.org.icongarden.bootstrap.require = function (dotPath)
		{
			dotPath = "extensions." + dotPath;

			cache.log ("requiring extension '" + dotPath + "'");

			for (var extensionIndex in extensionsArray)
			{
				var extension = extensionsArray [extensionIndex];
	
				if ('string' === typeof extension)
				{
					if (dotPath === extension)
					{
						return loadExtension (extensionIndex);
					}
				}
				else if (dotPath === extension.dotPath)
				{
					cache.log ("required extension '" + dotPath + "' was already loaded");
					return extension.childObject;
				}
			}
		}

		//	Iterate over all the extensions and load them.

		for (var extensionIndex in extensionsArray)
		{
			if ('string' === typeof extensionsArray [extensionIndex])
			{
				loadExtension (extensionIndex);
			}
			else
			{
				cache.log ("already loaded extension '" + extensionsArray [extensionIndex].dotPath + "'");
			}
		}

		// nobody needs to know these ever existed

		delete this.extensions.org.icongarden.bootstrap.nativePath;
		delete this.extensions.org.icongarden.bootstrap.log;

		// the C++ code is about to fork; let it clone a clean heap

		delete extensionsArray;
		delete cache;
		gc ( );

		return true;
	}
)
( );
